// Copyright (c) 2012, yuno. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
package sugarmintcandy.widgetwindow;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;

public class ResizeView extends View {
    private static final int DRAGGING_NONE = 0;
    private static final int DRAGGING_LEFT = 1;
    private static final int DRAGGING_TOP = 2;
    private static final int DRAGGING_RIGHT = 3;
    private static final int DRAGGING_BOTTOM = 4;
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mLayoutParams;
    private boolean mVisible;
    private Rect mBounds;
    private OnResizeListener mListener;
    private Drawable mBorderDrawable;
    private Rect mBorderPadding;
    private int mDraggingEdge;
    private int mStartValue;
    private float mTouchDownX;
    private float mTouchDownY;
    private int mMinimumWidth;
    private int mMinimumHeight;
    private int mMaximumWidth;
    private int mMaximumHeight;

    public interface OnResizeListener {
	void onSizeChanged(Rect bounds);

	void onResizeFinished();
    }

    public ResizeView(Context context) {
	super(context);
	mWindowManager = (WindowManager) context
		.getSystemService(Context.WINDOW_SERVICE);
	mLayoutParams = new WindowManager.LayoutParams();
	mLayoutParams.x = 0;
	mLayoutParams.y = 0;
	mLayoutParams.width = LayoutParams.MATCH_PARENT;
	mLayoutParams.height = LayoutParams.MATCH_PARENT;
	mLayoutParams.gravity = Gravity.CENTER;
	mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
	mLayoutParams.flags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
		| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
		| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
		| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
	mLayoutParams.alpha = 1;
	mLayoutParams.packageName = context.getPackageName();
	mLayoutParams.format = PixelFormat.TRANSLUCENT;
	mLayoutParams.windowAnimations = android.R.style.Animation_Toast;
	mBounds = new Rect();
	mBorderPadding = new Rect();
	mBorderDrawable = getResources().getDrawable(R.drawable.resize_border);
	mBorderDrawable.getPadding(mBorderPadding);
	DisplayMetrics dm = Utilities.getDisplayMetrics(mWindowManager);
	SharedPreferences prefs = PreferenceManager
		.getDefaultSharedPreferences(context);
	String titleBarSize = prefs.getString(PreferenceKeys.TITLE_BAR_SIZE,
		null);
	int titleBarHeight = Math.round(dm.density
		* Integer.parseInt(titleBarSize));
	mMinimumWidth = getResources().getDimensionPixelSize(
		R.dimen.minimum_width);
	mMinimumHeight = getResources().getDimensionPixelSize(
		R.dimen.minimum_height)
		+ titleBarHeight;
	mMaximumWidth = dm.widthPixels - 1;
	mMaximumHeight = dm.heightPixels - Math.round(32 * dm.density);
    }

    public void show() {
	if (!mVisible) {
	    mWindowManager.addView(this, mLayoutParams);
	    mVisible = true;
	}
    }

    public void hide() {
	if (mVisible) {
	    mWindowManager.removeView(this);
	    mVisible = false;
	}
    }

    public void setBounds(Rect bounds) {
	if (!bounds.equals(mBounds)) {
	    mBounds = bounds;
	    invalidate();
	}
    }

    public void setOnResizeListener(OnResizeListener listener) {
	mListener = listener;
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
	if (mBounds == null || mBorderDrawable == null) {
	    return;
	}
	Rect dst = new Rect();
	dst.left = mBounds.left - mBorderPadding.left;
	dst.top = mBounds.top - mBorderPadding.top;
	dst.right = mBounds.right + mBorderPadding.right;
	dst.bottom = mBounds.bottom + mBorderPadding.bottom;
	mBorderDrawable.setBounds(dst);
	mBorderDrawable.draw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
	int action = event.getAction() & MotionEvent.ACTION_MASK;
	if (action == MotionEvent.ACTION_DOWN) {
	    float x = event.getX();
	    float y = event.getY();
	    mTouchDownX = x;
	    mTouchDownY = y;
	    mDraggingEdge = findEdge(x, y);
	    switch (mDraggingEdge) {
	    case DRAGGING_LEFT:
		mStartValue = mBounds.left;
		break;
	    case DRAGGING_TOP:
		mStartValue = mBounds.top;
		break;
	    case DRAGGING_RIGHT:
		mStartValue = mBounds.right;
		break;
	    case DRAGGING_BOTTOM:
		mStartValue = mBounds.bottom;
		break;
	    default:
		if (mListener != null) {
		    mListener.onResizeFinished();
		}
		break;
	    }
	}
	if (action == MotionEvent.ACTION_MOVE) {
	    if (mDraggingEdge != DRAGGING_NONE) {
		float dx = event.getX() - mTouchDownX;
		float dy = event.getY() - mTouchDownY;
		switch (mDraggingEdge) {
		case DRAGGING_LEFT:
		    mBounds.left = Math.min(
			    mBounds.right - mMinimumWidth,
			    Math.max(mBounds.right - mMaximumWidth,
				    Math.round(mStartValue + dx)));
		    break;
		case DRAGGING_RIGHT:
		    mBounds.right = Math.max(
			    mBounds.left + mMinimumWidth,
			    Math.min(mBounds.left + mMaximumWidth,
				    Math.round(mStartValue + dx)));
		    break;
		case DRAGGING_TOP:
		    mBounds.top = Math.min(
			    mBounds.bottom - mMinimumHeight,
			    Math.max(mBounds.bottom - mMaximumHeight,
				    Math.round(mStartValue + dy)));
		    break;
		case DRAGGING_BOTTOM:
		    mBounds.bottom = Math.max(
			    mBounds.top + mMinimumHeight,
			    Math.min(mBounds.top + mMaximumHeight,
				    Math.round(mStartValue + dy)));
		    break;
		}
		invalidate();
		if (mListener != null) {
		    mListener.onSizeChanged(mBounds);
		}
	    }
	}
	if (action == MotionEvent.ACTION_CANCEL
		|| action == MotionEvent.ACTION_UP) {
	    mDraggingEdge = DRAGGING_NONE;
	}
	return true;
    }

    private int findEdge(float x, float y) {
	if (x >= mBounds.left - mBorderPadding.left && x < mBounds.left
		&& y >= mBounds.top && y < mBounds.bottom) {
	    return DRAGGING_LEFT;
	} else if (x >= mBounds.right
		&& x < mBounds.right + mBorderPadding.right && y >= mBounds.top
		&& y < mBounds.bottom) {
	    return DRAGGING_RIGHT;
	} else if (y >= mBounds.top - mBorderPadding.top && y < mBounds.top
		&& x >= mBounds.left && x < mBounds.right) {
	    return DRAGGING_TOP;
	} else if (y >= mBounds.bottom
		&& y < mBounds.bottom + mBorderPadding.bottom
		&& x >= mBounds.left && x < mBounds.right) {
	    return DRAGGING_BOTTOM;
	} else {
	    return DRAGGING_NONE;
	}
    }
}
